'''
Created on Feb 11, 2011

@author: Jonathan Herbst
'''

import ConfigParser, datetime, os, random, string
from threading import Lock, RLock, Semaphore, Thread, Condition

_keySet = string.ascii_letters + string.digits + "!@#$%^&*~()-_+=[]{}|"
_keyLength = 64

def RandomKey():
    return string.join(random.sample(_keySet, _keyLength), '')

class ClearAfterWaitEvent():
    
    # After Tim Peters' event class (without is_posted())

    def __init__(self):
        self.__cond = Condition(Lock())
        self.__flag = False

    def isSet(self):
        return self.__flag

    is_set = isSet

    def set(self):
        self.__cond.acquire()
        try:
            self.__flag = True
            self.__cond.notify_all()
        finally:
            self.__cond.release()

    def clear(self):
        self.__cond.acquire()
        try:
            self.__flag = False
        finally:
            self.__cond.release()

    def wait(self, timeout=None):
        self.__cond.acquire()
        try:
            if not self.__flag:
                self.__cond.wait(timeout)
            self.__flag = False
        finally:
            self.__cond.release()

class BlockingPopThreadSafeQueue():
    
    def __init__(self):
        self.m_list = []
        self.m_lock = Lock()
        self.m_semaphore = Semaphore(0)
        
    def push(self, object):
        self._Lock()
        self.m_list.append(object)
        self._Release()
        self.m_semaphore.release()
        
    def pop(self):
        object = None
        self.m_semaphore.acquire()
        self._Lock()
        object = self.m_list.pop(0)
        self._Release()
        return object
        
    def _Lock(self):
        self.m_lock.acquire()
        
    def _Release(self):
        self.m_lock.release()
        
class SimpleConfigParser():
    
    def __init__(self):
        self.m_configParser = ConfigParser.ConfigParser()
        
    def Read(self, fileName):
        self.m_configParser.read(fileName)
        
    def Write(self, fileName):
        fp = open(fileName, 'w')
        self.m_configParser.write(fp)
        fp.close()
        
    def Set(self, section, option, value):
        try:
            self.m_configParser.set(section, option, value)
            return True
        except:
            return False
        
    def Get(self, section, option):
        try:
            return self.m_configParser.get(section, option)
        except:
            return None
        
    def AddSection(self, section):
        try:
            self.m_configParser.add_section(section)
            return True
        except:
            return False

class ThreadedConfigSynchronizer(Thread, SimpleConfigParser):

    def __init__(self, fileName):
        self.t_changedEvent = ClearAfterWaitEvent()
        self.t_lock = Lock()
        self.t_exit = False
        
        self.m_fileName = fileName
        
        Thread.__init__(self)
        SimpleConfigParser.__init__(self)
        
        self.Read(self.m_fileName)
        self.start()
        
    def Set(self, section, option, value):
        ret = self._ExecuteLocked(SimpleConfigParser.Set, self, section, option, value)
        self.t_changedEvent.set()
        return ret
        
    def Get(self, section, option):
        return self._ExecuteLocked(SimpleConfigParser.Get, self, section, option)
        
    def AddSection(self, section):
        ret = self._ExecuteLocked(SimpleConfigParser.AddSection, self, section)
        self.t_changedEvent.set()
        return ret
        
    def Exit(self):
        self.t_exit = True
        self.t_changedEvent.set()
        
    def run(self):
        while not self.t_exit:
            self.t_changedEvent.wait()
            self._Write()
    
    def _Write(self):
        self._ExecuteLocked(SimpleConfigParser.Write, self, self.m_fileName)
    
    def _ExecuteLocked(self, fcn, *argv, **argd):
        exception = None
        ret = None
        self._Acquire()
        try:
            ret = fcn(*argv, **argd)
        except Exception, e:
            exception = e
        self._Release()
        if exception is not None:
            raise exception
        return ret
    
    def _Acquire(self):
        self.t_lock.acquire()
        
    def _Release(self):
        self.t_lock.release()
        
class LogFile():
    
    # Log message types
    FATAL = 0
    ERROR = 1
    WARNING = 2
    INFORMATION = 3
    DEBUG = 4
    
    _fileLocks = {}
    _fileLocksLock = Lock()
    
    # log directory class variable
    _logDirectory = ""
    _logThreshold = 3
    
    # message templates
    _messageTemplate = "%s [%s]!%s %s\n"
    _dateTemplate = "%m/%d/%Y(%H:%M:%S)"
    
    # map from message type to a string representation
    _messageTypeStrings = {FATAL: "Fatal",
                           ERROR: "Error",
                           WARNING: "Warning",
                           INFORMATION: "Info",
                           DEBUG: "Debug",
                           }
    
    def __init__(self, fileName, logThreshold=None, logDirectory=None):
        """
        Initialize the instance and some class variables
        
        Parameters:
        fileName - the name of the file to write the log to
        logThreshold (class) - the threshold of log type to log
            Warning: this parameter sets for all instances
        logDirectory (class) - the directory to write the log files in
            Warning: this parameter sets for all instances
        """
        # setup log directory for all instances
        if logDirectory:
            logDirectory = str(logDirectory)
            if len(logDirectory) > 0:
                if logDirectory[-1] is not "/":
                    logDirectory += "/"
            LogFile._logDirectory = logDirectory
            self._CreateLogDirIfNotExists()
        # setup log threshold for all instances
        if logThreshold:
            logThreshold = int(logThreshold)
            LogFile._logThreshold = logThreshold
            
        # setup the file name
        self.m_fileName = self._logDirectory + fileName
        self.m_file = None
        
        # load the file lock
        LogFile._fileLocksLock.acquire()
        try:
            if self.m_fileName not in self._fileLocks:
                self.m_fileLock = Lock()
                LogFile._fileLocks[self.m_fileName] = self.m_fileLock
            else:
                self.m_fileLock = self._fileLocks[self.m_fileName]
        finally:
            LogFile._fileLocksLock.release()
            
    def Close(self):
        """
        Close the file if it's not already closed (Will never throw an exception)
        """
        try:
            if self.m_file:
                self.m_file.close()
                self.m_file = None
        except:
            print "Log File Exception"
            
    def Write(self, context, message, logLevel=INFORMATION):
        """
        Write a log message to the log file (Will never throw an exception)
        """
        try:
            # make sure logLevel is an int
            logLevel = int(logLevel)
            
            # don't write the message if the log level is
            if logLevel > self._logThreshold:
                return
            
            context = str(context)
            message = str(message)
            
            # format the message
            now = datetime.datetime.now()
            nowStr = now.strftime(self._dateTemplate)
            logLevelText = str(logLevel)
            if logLevel in self._messageTypeStrings:
                logLevelText = self._messageTypeStrings[logLevel]
            formattedMessage = self._messageTemplate % (nowStr, context, logLevelText, message)
            
            # open and write the message
            self._Open()
            self.m_fileLock.acquire()
            try:
                self.m_file.write(formattedMessage)
                self.m_file.flush()
            finally:
                self.m_fileLock.release()
        except:
            print "Log File Exception"
        
    def _CreateLogDirIfNotExists(self):
        """
        Create the log directory if it doesn't already exist
        """
        LogFile._fileLocksLock.acquire()
        try:
            if len(self._logDirectory) > 0:
                if not os.path.exists(self._logDirectory):
                    os.makedirs(self._logDirectory)
        finally:
            LogFile._fileLocksLock.release()
        
    def _Open(self):
        """
        Open the file if it's not already open
        """
        if not self.m_file:
            self._CreateLogDirIfNotExists()
            self.m_file = open(self.m_fileName, 'a')
            
def TestLogFile():
    class LogThread(Thread):
        def __init__(self, threadNum):
            self.m_threadNum = threadNum
            if threadNum == 0:
                self.m_log = LogFile("TestLog", logDirectory="TestLogDir", logThreshold=1)
            else:
                self.m_log = LogFile("TestLog")
            Thread.__init__(self)
            
        def run(self):
            for i in range(1000):
                self.m_log.Write("Test", "Message", logLevel=2)
            self.m_log.Close()
    
    f = open("TestLogDir/TestLog", 'w')
    f.close()
    numThreads = 10
    threadList = []
    for i in range(numThreads):
        threadList.append(LogThread(i))
    
    for thread in threadList:
        thread.start()
        
    for thread in threadList:
        thread.join()
        
if __name__ == "__main__":
    TestLogFile()

        